Refresh the video calling screens with the design system tokens - #1827
Refresh the video calling screens with the design system tokens#1827andremion wants to merge 10 commits into
Conversation
The call app bar back button becomes a StreamIconButton, and its recording dot border and center text use the stroke token and the body text style. The remaining literal paddings and radii on the participant label, the generic indicator and the spotlight containers move to tokens. The reaction emoji animates to the emoji size token. The floating self view takes the 144x208 size tokens on device and in the preview, with one radius for its shape and clip. Avatars of 80dp and larger rendered their initials with the 14sp badge style, because no token text style exists for that size. Their initials now scale to 40 percent of the avatar, so the 100dp tile fallback and the 200dp ringing avatars read at a glance. Goldens change for the floating self view size and for the initials of the 80dp and 100dp avatars.
Content paddings, the app bar padding and spacer, the floating self view clip, the participant actions padding and the bad network label move to StreamTokens and the theme typography. The share panel gets the same treatment: its paddings and radii move to tokens, the add-user and copy icons become their design system equivalents, and the QR caption is explicitly white because the QR box is black in both themes. The popup close button becomes a StreamIconButton with the design system X, in the text color instead of white. Layout and behavior are unchanged.
The SDK call activity content already pads for the system bars, but only MainActivity opted into edge to edge. Below API 35 the call screen kept the opaque status and navigation bar bands.
The toggle received the negated menu state, so the active style applied while the menu was closed. On the old theme the active and inactive styles had the same dark background, which hid the mistake. The design system active style is the brand color, so the button looked pressed all the time.
The design system rebuild set the call actions to the 40dp Medium size. The 1.x SDK drew them at 44dp, and the Figma call controls and app bar buttons are 48dp, which is the existing Large size. Every call action and the app bar back button now default to Large. Callers that pass a size keep it.
The design system rebuild put the sliders icon on both states of the toggle, which reads as a settings control. The icon set has no music glyph, so the toggle goes back to the material music note and music off icons the 1.x SDK used, vendored as vector drawables in ui-core like the other material icons there.
The design system maps the secondary button background to the surface-default color. The ringing screens and the audio call screen painted their full-screen background with that same color, so the mic, camera and speaker buttons had no visible shape. Those screens now use the app background, like every other full screen in the demo app. The share panel keeps the surface color because it is a card, so its Call ID button uses the outlined secondary style instead.
|
@CodeRabbit review |
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
✅ Action performedReview finished.
|
SDK Size Comparison 📏
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (62)
📒 Files selected for processing (30)
💤 Files with no reviewable changes (1)
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review. WalkthroughThe pull request aligns call UI components with Stream design tokens, shared controls, resource-backed icons, larger default buttons, updated backgrounds, proportional avatar text, and revised participant rendering behavior. ChangesCall UI design-system alignment
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~25 minutes Change: Refactor Suggested reviewers: Merge Risk: ⚪ Minimal · up to The call UI refresh preserves the reviewed control wiring and visual behavior, with validation completed across call states and themes. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 19.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 36 functions across 27 files. (2 skipped: 2 unsupported.)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. A rabbit hops through tokens bright Comment |
| val scaled = with(LocalDensity.current) { | ||
| (this@toAvatarTextStyle * LARGE_AVATAR_TEXT_RATIO).toSp() | ||
| } | ||
| val fontSize = if (scaled > LARGE_AVATAR_MAX_TEXT_SIZE) LARGE_AVATAR_MAX_TEXT_SIZE else scaled |
There was a problem hiding this comment.
Non-blocking, but the ceiling here isn't absolute.
Dp.toSp() is (value / fontScale).sp, so scaled has already had the user's font scale divided out. Comparing that against a fixed 48.sp makes the effective ceiling move with the font-scale setting instead of pinning it at 48dp:
- font scale 1.0 →
scaled= 80sp → capped → initials render at 48dp - font scale 2.0 →
scaled= 40sp → under the cap → initials render at ~80dp
On the 200dp avatar in ParticipantAvatars.kt that's a visibly different result from what LARGE_AVATAR_MAX_TEXT_SIZE's KDoc promises. Clamping in Dp before the conversion keeps it absolute:
val capped = minOf(this@toAvatarTextStyle * LARGE_AVATAR_TEXT_RATIO, LARGE_AVATAR_MAX_TEXT_SIZE)
val fontSize = with(LocalDensity.current) { capped.toSp() }
typography.headingLarge.copy(fontSize = fontSize, lineHeight = fontSize)with LARGE_AVATAR_MAX_TEXT_SIZE becoming 48.dp. At font scale 1 the output is identical, so the re-recorded snapshots stay valid — I verified verifyPaparazziDebug passes either way.
There was a problem hiding this comment.
Good catch, you're right. Fixed in 072ab8e, clamping in Dp before the conversion like you suggested. Goldens verify without re-recording, so font scale 1 is unchanged.
| @@ -78,6 +81,18 @@ internal fun Dp.toAvatarTextStyle(): TextStyle { | |||
| this < StreamTokens.size32 -> typography.captionEmphasis | |||
| this < StreamTokens.size48 -> typography.bodyEmphasis | |||
| this < StreamTokens.size80 -> typography.headingLarge | |||
There was a problem hiding this comment.
Not for this PR — a parity note, since Chat and Video are now on the same design tokens.
Chat's equivalent ladder (AvatarPlaceholder.kt, Dp.toPlaceholderTextStyle()) picks a different style in two of the bands both SDKs share:
| avatar size | Chat | here |
|---|---|---|
| 32–39dp | captionEmphasis |
bodyEmphasis |
| 48–79dp | headingMedium |
headingLarge |
Chat also keys off named AvatarSize constants rather than raw size tokens, which gives consumers a size vocabulary to match against.
Related: the reason the else branch has to compute anything is that numericExtraLarge is fontSizeSm (14sp) despite its name — and it's the same in Chat. If that token were fixed in both SDKs this could go back to being a plain lookup, which is the pattern Chat follows throughout.
There was a problem hiding this comment.
Took the band alignment in 40af253, video follows chat now below 80dp. Four goldens move and it's 20sp to 18sp on the initials, under one percent of the pixels.
One thing on numericExtraLarge though: I checked the design source and numeric.xl maps to font-size.sm there, so the 14sp is intentional. That scale is micro, xxs, xs, sm for badge counts, so extra large means the biggest badge rather than a big heading. Both SDKs read it correctly. The else branch still has to compute because our avatars go up to 200dp and chat tops out at 80dp.
I'll flag the ladder with Jurgen so he can correct both SDKs if chat is the one that's off.
| .background(color = VideoTheme.colors.backgroundCoreApp), | ||
| ) { | ||
| Column( | ||
| modifier = modifier |
There was a problem hiding this comment.
Pre-existing, not introduced here — flagging it only because it's adjacent to the line you changed.
The inner Column re-applies modifier, which the outer Box two lines up already has. A caller passing AudioOnlyCallContent(modifier = Modifier.padding(24.dp)) gets the padding twice, and a caller-supplied .background(...) would repaint over the backgroundCoreApp this PR just introduced. Should be Modifier. Same pattern at lines 113/119.
There was a problem hiding this comment.
Fixed in 7aa1e86, both sites. Pre-existing as you say, but the background change makes the repaint case real, so it seemed worth taking here.
Dp.toSp divides the font scale out, so comparing the converted value against a fixed 48.sp let the ceiling grow with the user's font size setting. On the 200dp ringing avatar the initials rendered at about 80dp at font scale 2, instead of the 48dp the constant promises. The clamp now happens in Dp, before the conversion. The output at font scale 1 is unchanged, so the snapshots stay as recorded.
The box and the column inside it both took the caller's modifier, so a caller passing padding got it twice, and a caller passing a background painted over the screen background. The details slot had the same problem. The inner layouts now start from Modifier. Found by the review on the call screen refresh. It predates that work, but the background change makes the repaint case visible.
Chat picks a different text style in two of the bands the two SDKs share: caption for 32 to 39dp and a medium heading for 48 to 79dp. Nothing asked for the difference, so video follows chat now. The computed branch above 80dp stays, because chat has no avatar that large. Four goldens move, all in the 48 to 79dp band: the 72dp preview, the 48dp invite row and the audio room grid. The initials go from 20sp to 18sp, which is under one percent of the pixels in each image.
|



Goal
Closes AND-1425
The in-call screens move to the design system tokens and the base components from AND-1420. This is a refresh, not a redesign, so the layout and the functionality stay as they are on
develop-v2. The scope rules are on the parent issue AND-1421.Implementation
SDK:
CallAppBarusesStreamIconButtonfor the back button, a stroke token for the recording dot and a typography token for the center text.FloatingParticipantVideouses the size and radius tokens and keeps its drag behavior.Largesize. The base component rebuild had left them at 40dp, the 1.x SDK drew 44dp and Figma asks for 48dp.Demo app:
MainActivitydoes since AND-1513.🎨 UI Changes
1.x on the left, this branch on the right.
Lobby
In call
In call, dark mode
Camera off, participant initials
Camera off, participant initials, dark mode
Share panel
Share panel, dark mode
Testing
Ran on two emulators, a Pixel 4 on API 30 with the 1.x demo app and a Pixel 8 on API 35 with this branch, in a shared call. Checked the lobby, the call grid, the camera-off tiles, the muted state, the share panel and the ringing screens, in light and dark mode.
To reproduce: start a test call on one device, join it from a second one, then open the share panel and toggle the camera and the microphone on both.
Validation on the final tree:
All green. The snapshots were re-recorded once, deliberately, as the parent issue allows. There is no
detekttask in this repo.Summary by CodeRabbit
New Features
UI Improvements